Rebonds SSH ou hôtes bastions

Sommaire

Utilisation de ProxyJump

Utilisation de ProxyCommand

 

De nos jours, il est devenu habituel d’avoir ce que l’on appelle un hôte bastion. Ce serveur est généralement dans un sous-réseau public et sert de passerelle SSH vers un sous-réseau privé. Les hôtes bastions sont essentiellement utilisés pour séparer les activités privées d’une attaque venant de l’extérieur, tout en fournissant un service accessible depuis l’extérieur. En l’occurence, un service SSH est souvent proposé à des prestataires extérieurs pour leur donner la possibilité d’interagir avec une machine avec l’obtention un pseudo-terminal.

 
 

Il est parfois nécessaire de passer successivement par plusieurs hôtes bastions pour d’atteindre le serveur avec lequel on souhaite interagir.

 
 

Ainsi, en se connectant en SSH successivement à chaque serveur intermédiaire il est possible d’atteindre le serveur cible. Cependant, il est possible d’automatiser l’ensemble des connexions intermédiaires. Pour ce faire, il existe plusieurs méthodes, soit on utilise la fonctionnalité ProxyCommand, qui offre beaucoup de souplesse en contre partie d’une certaine complexité, soit la fonctionnalité plus récente ProxyJump qui est bien plus simple à appréhender.

 
 

Dans tous les exemples qui suivront, nous nous placerons en tant que l’utilisateur Bob, avec la configuration de départ suivante :

bob:~$ ls -l ~/.ssh

total 16

-rw-------  1 demo  demo  387 May  1 11:07 id_ed25519

-rw-r--r--  1 demo  demo   85 May  1 11:07 id_ed25519.pub

 

bob:~$ cat ~/.ssh/id_ed25519.pub

ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAcqaPvZXFHgHzqOplzzmRg4AnxUYpFO0r4LrtFGsC/i bob

Pour illustrer nos propros, nous prendrons également l’architecture suivante comme exemple.

 
 

Configuration du serveur bastion A :

bob:~$ ssh admin@server_A

The authenticity of host 'server_a (140.82.53.221)' can't be established.

ED25519 key fingerprint is SHA256:FRO3JsAygUYpREAWWJR75wi6VLqIXdHi2uohJ3Fc0k4.

Are you sure you want to continue connecting (yes/no/[fingerprint])? yes

Warning: Permanently added 'server_a,140.82.53.221' (ED25519) to the list of known hosts.

Last login: Sat May  1 09:29:36 2021 from 176.160.93.220

admin@a:~$

 

admin@a:~$ ip a | grep "inet "

    inet 127.0.0.1/8 scope host lo

    inet 140.82.53.221/23 brd 140.82.53.255 scope global dynamic ens0

    inet 10.0.0.1/24 scope global ens1

 

admin@a:~$ ls -l ~/.ssh/

total 12

-rw-r--r-- 1 admin admin  85 May  1 09:28 authorized_keys

-rw------- 1 admin admin 399 May  1 09:33 id_ed25519

-rw-r--r-- 1 admin admin  90 May  1 09:33 id_ed25519.pub

 

admin@a:~$ cat ~/.ssh/id_ed25519.pub

ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJAKbCinjGw/0BKTZnGP182A2XdvYD5mfTOz8TB06g23 server_a

 

admin@a:~$ cat ~/.ssh/authorized_keys

ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAcqaPvZXFHgHzqOplzzmRg4AnxUYpFO0r4LrtFGsC/i bob

Configuration du serveur B :

admin@a:~$ ssh admin@server_B

The authenticity of host 'server_b (10.0.0.2)' can't be established.

ED25519 key fingerprint is SHA256:KmxCNSOZGgulC9c4BfCwY4Sj3+n3Oe56hvr4Uvlxyv4.

Are you sure you want to continue connecting (yes/no)? yes

Warning: Permanently added 'server_b,10.0.0.2' (ED25519) to the list of known hosts.

Last login: Sat May  1 08:56:36 2021 from 176.160.93.220

admin@b:~$

 

admin@b:~$ ip a | grep "inet "

    inet 127.0.0.1/8 scope host lo

    inet 10.0.0.2/24 scope global ens0

 

admin@b:~$ ls -l ~/.ssh/

total 12

-rw-r--r-- 1 admin admin  90 May  1 09:34 authorized_keys

-rw------- 1 admin admin 399 May  1 09:34 id_ed25519

-rw-r--r-- 1 admin admin  90 May  1 09:34 id_ed25519.pub

 

admin@b:~$ cat ~/.ssh/id_ed25519.pub

ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOqDONyCJcsoBiKz7r53dvQMgjGaS/OELNq/gfAUDGx4 server_b

 

admin@b:~$ cat ~/.ssh/authorized_keys

ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJAKbCinjGw/0BKTZnGP182A2XdvYD5mfTOz8TB06g23 server_a

Utilisation de ProxyJump

Au lieu de se connecter d’abord en SSH à l’hôte du bastion A, puis d’utiliser à nouveau OpenSSH sur le bastion pour se connecter à l’hôte distant BOpenSSH peut, depuis la version 7.3, créer lui-même les première et deuxième connexions en utilisant ProxyJump. On peut définir des noms d’utilisateur et des ports spécifiques s’ils diffèrent entre les hôtes :

$ ssh -J user@<bastion:port> <user@remote:port>

Cependant, il est important de noter que l’usage exclusif de l’authentification par clés cryptographiques requiert que notre clé publique soit positionné à la fois sur le serveur bastion A et sur le serveur de destination B. Actuellement, ce n’est pas le cas, aussi, nous faisont face à ce message d’erreur :

bob:~$ ssh -J admin@server_A admin@server_B

admin@server_b: Permission denied (publickey,keyboard-interactive).

Ce qui signifie que la premier tunnel SSH a bien été établi, mais que le second échoue à l’authentification. Il est donc primordial de pousser notre clé publique SSH sur l’ensemble des serveurs intermédiaires et sur le serveur de destination si l’on souhaite pouvoir utiliser convenablement la fonctionnalité ProxyJump. Nous verrons plus loin, qu’avec la méthode ProxyCommand, il est possible de récupérer successivement les identités des serveurs intermédiaires afin de ne pas être contraint de pousser notre clé publique SSH sur chaque serveurs.

Nous poussons donc notre clé publique SSH sur le serveur destination B.

bob:~$ cat ~/.ssh/id_ed25519.pub | ssh admin@server_A "cat | ssh admin@server_B \"cat >> .ssh/authorized_keys\""

Nous pouvons à présent nous connecter directement sur le serveur B.

bob:~$ ssh -J admin@server_A admin@server_B

Last login: Sat May  1 09:36:22 2021 from 10.0.0.1

admin@b:~$

 

admin@b:~$ cat ~/.ssh/authorized_keys

ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJAKbCinjGw/0BKTZnGP182A2XdvYD5mfTOz8TB06g23 server_a

ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAcqaPvZXFHgHzqOplzzmRg4AnxUYpFO0r4LrtFGsC/i bob

Cette fonctionnalité est très pratique, mais on peut aller encore plus loin en l’utilisant directement depuis le fichier de configuration de notre client OpenSSH.

bob:~$ cat << EOF > ~/.ssh/config

> Host server_A

>   Hostname server_A

>   User admin

>   PubkeyAuthentication yes

>   IdentityFile ~/.ssh/id_ed25519

>

> Host server_B

>   Hostname server_B

>   User admin

>   IdentityFile ~/.ssh/id_ed25519

>   ProxyJump server_A

> EOF

 

bob:~$ ssh server_B

Last login: Sat May  1 09:59:47 2021 from 10.0.0.1

admin@b:~$

Un avantage indéniable est de pouvoir ici spécifier des identités différentes selon le serveur intermédiaire sur lequel on souhaite rebondir.

Utilisation de ProxyCommand

ProxyJump est la manière simplifiée d’utiliser une fonctionnalité que le client OpenSSH possède depuis longtemps : ProxyCommand. Elle fonctionne en transférant les entrées standard (stdin) et les sorties standard (stdout) de la machine distante à travers les hôtes intermédiaires. Pour ce faire, on doit spécifier la commande qui sera exécuté sur le serveur intermédiaire.

$ ssh -o ProxyCommand="ssh -W %h:%p bastion" user@<remote:port>

Les arguments %h:%p de l’option -W ci-dessus spécifient la transmission des entrées et sorties standard vers l’hôte distant, identifié par %h et le port de l’hôte distant, identifié par %p.

Comme précédemment, il est nécessaire que nous poussions notre clé publique SSH sur le serveur destination B, pour que l’authentification sur le serveur B puisse s’effectuer correctement.

bob:~$ ssh -o ProxyCommand="ssh -W %h:%p admin@server_A" admin@server_B

admin@server_b: Permission denied (publickey,keyboard-interactive).

 

bob:~$ cat ~/.ssh/id_ed25519.pub | ssh admin@server_A "cat | ssh admin@server_B \"cat >> .ssh/authorized_keys\""

 

bob:~$ ssh -o ProxyCommand="ssh -W %h:%p admin@server_A" admin@server_B

Last login: Sat May  1 10:07:56 2021 from 10.0.0.1

admin@b:~$

De même que pour ProxyJump il est possible d’écrire un fichier de configuration pour notre client OpenSSH.

bob:~$ cat << EOF > ~/.ssh/config

Host server_A

  Hostname server_A

  User admin

  PubkeyAuthentication yes

  IdentityFile ~/.ssh/id_ed25519

 

Host server_B

  Hostname server_B

  User admin

  IdentityFile ~/.ssh/id_ed25519

  ProxyCommand ssh -W %h:%p server_A

EOF

 

bob:~$ ssh server_B

Last login: Sat May  1 13:46:28 2021 from 10.0.0.1

admin@b:~$

On peut là aussi spécifier des identités différentes selon le serveur intermédiaire sur lequel on souhaite rebondir. Très pratique.

Maintenant, voyons comment éviter d’avoir à pousser notre clé publique SSH sur l’ensemble des serveurs qui se trouvent après le premier serveur de rebond. L’idée, c’est de tirer partie des identités déjà présente sur chaque intermédiaire. Pour se faire, les seules conditions que nous devons remplir sont :

On supprime donc notre configuration précédente, ainsi que notre clé publique SSH sur le serveur de destination B.

bob:~$ ssh server_B sed -i '/bob/d' .ssh/authorized_keys

 

bob:~$ rm ~/.ssh/config

Partant de la configuration initiale, nous savons que les deux premières conditions sont déjà remplies. Vérifions maintenant que le serveur OpenSSH du serveur A ait bien l’option AllowAgentForwarding activée.

bob:~$ ssh -tt admin@server_A "sudo sshd -T | grep allowagentforwarding"

[sudo] password for admin:

allowagentforwarding yes

Connection to server_a closed.

On lance notre agent OpenSSH.

bob:~$ eval `ssh-agent -s`

Agent pid 71349

 

bob:~$ ssh-add

Identity added: /home/demo/.ssh/id_ed25519 (bob)

 

bob:~$ ssh-add -l

256 SHA256:p/0ADeqgtj7KNIDKmLME0SPQYKC4cbYMVmBT+1deBAA bob (ED25519)

On peut maintenant se connecter directement au serveur B en profitant de l’identité du serveur A.

bob:~$ ssh -o ProxyCommand="ssh -o ForwardAgent=yes admin@server_A 'ssh-add && nc %h %p'" admin@server_B

Identity added: /home/admin/.ssh/id_ed25519 (server_a)

Last login: Sun May  2 19:09:40 2021 from 10.0.0.1

admin@b:~$

Cependant, comme on peut le voir, l’identité du serveur A a été ajouté à notre agent OpenSSH local. On peut facilement le vérifier.

bob:~$ ssh-add -l

256 SHA256:p/0ADeqgtj7KNIDKmLME0SPQYKC4cbYMVmBT+1deBAA bob (ED25519)

256 SHA256:0TX2zjs3UnwFyC1z0/syQ/qnq7Ora1rDVdpEqS0VFaY server_a (ED25519)

Aussi, il est primordial de comprendre ici que la clé privée SSH du serveur A, n’est pas redescendu jusque notre machine, seulement sa capacité d’authentification.

C’est quelque chose qui faut prendre en compte, car si il s’agit de la capacité d’authentification bastion d’un partenaire, il serait cohérent que celui-ci refuse que celle-ci sorte de son périmètre.

Il est néanmoins fortement déconseillé de faire usage de la fonctionnalité ForwardAgent en raison de tous les problèmes de compromision que cela peut induire si mal utilisée. Il est donc préférable de demander le positionnement de notre clé publique personnelle sur l’ensemble des serveurs sur lesquels nous avons le droit de nous authentifier, et d’utiliser la fonctionnalité ProxyJump.

Nous verrons dans un prochain article comment une mauvaise utilisation de la redirection de port avec OpenSSH peut entraîner la compromission de notre propre système d’information.